/*
 * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */

package com.sun.security.auth.callback;

/* JAAS imports */

import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.ConfirmationCallback;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.TextOutputCallback;
import javax.security.auth.callback.UnsupportedCallbackException;

/* Java imports */
import java.awt.Component;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.swing.Box;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPasswordField;
import javax.swing.JTextField;

/**
 * <p>
 * Uses a Swing dialog window to query the user for answers to
 * authentication questions.
 * This can be used by a JAAS application to instantiate a
 * CallbackHandler
 *
 * @see javax.security.auth.callback
 * @deprecated This class will be removed in a future release.
 */
@jdk.Exported(false)
@Deprecated
public class DialogCallbackHandler implements CallbackHandler {

    /* -- Fields -- */

  /* The parent window, or null if using the default parent */
  private Component parentComponent;
  private static final int JPasswordFieldLen = 8;
  private static final int JTextFieldLen = 8;

    /* -- Methods -- */

  /**
   * Creates a callback dialog with the default parent window.
   */
  public DialogCallbackHandler() {
  }

  /**
   * Creates a callback dialog and specify the parent window.
   *
   * @param parentComponent the parent window -- specify <code>null</code> for the default parent
   */
  public DialogCallbackHandler(Component parentComponent) {
    this.parentComponent = parentComponent;
  }

  /*
   * An interface for recording actions to carry out if the user
   * clicks OK for the dialog.
   */
  private static interface Action {

    void perform();
  }

  /**
   * Handles the specified set of callbacks.
   *
   * @param callbacks the callbacks to handle
   * @throws UnsupportedCallbackException if the callback is not an instance  of NameCallback or
   * PasswordCallback
   */

  public void handle(Callback[] callbacks)
      throws UnsupportedCallbackException {
        /* Collect messages to display in the dialog */
    final List<Object> messages = new ArrayList<>(3);

        /* Collection actions to perform if the user clicks OK */
    final List<Action> okActions = new ArrayList<>(2);

    ConfirmationInfo confirmation = new ConfirmationInfo();

    for (int i = 0; i < callbacks.length; i++) {
      if (callbacks[i] instanceof TextOutputCallback) {
        TextOutputCallback tc = (TextOutputCallback) callbacks[i];

        switch (tc.getMessageType()) {
          case TextOutputCallback.INFORMATION:
            confirmation.messageType = JOptionPane.INFORMATION_MESSAGE;
            break;
          case TextOutputCallback.WARNING:
            confirmation.messageType = JOptionPane.WARNING_MESSAGE;
            break;
          case TextOutputCallback.ERROR:
            confirmation.messageType = JOptionPane.ERROR_MESSAGE;
            break;
          default:
            throw new UnsupportedCallbackException(
                callbacks[i], "Unrecognized message type");
        }

        messages.add(tc.getMessage());

      } else if (callbacks[i] instanceof NameCallback) {
        final NameCallback nc = (NameCallback) callbacks[i];

        JLabel prompt = new JLabel(nc.getPrompt());

        final JTextField name = new JTextField(JTextFieldLen);
        String defaultName = nc.getDefaultName();
        if (defaultName != null) {
          name.setText(defaultName);
        }

                /*
                 * Put the prompt and name in a horizontal box,
                 * and add that to the set of messages.
                 */
        Box namePanel = Box.createHorizontalBox();
        namePanel.add(prompt);
        namePanel.add(name);
        messages.add(namePanel);

                /* Store the name back into the callback if OK */
        okActions.add(new Action() {
          public void perform() {
            nc.setName(name.getText());
          }
        });

      } else if (callbacks[i] instanceof PasswordCallback) {
        final PasswordCallback pc = (PasswordCallback) callbacks[i];

        JLabel prompt = new JLabel(pc.getPrompt());

        final JPasswordField password =
            new JPasswordField(JPasswordFieldLen);
        if (!pc.isEchoOn()) {
          password.setEchoChar('*');
        }

        Box passwordPanel = Box.createHorizontalBox();
        passwordPanel.add(prompt);
        passwordPanel.add(password);
        messages.add(passwordPanel);

        okActions.add(new Action() {
          public void perform() {
            pc.setPassword(password.getPassword());
          }
        });

      } else if (callbacks[i] instanceof ConfirmationCallback) {
        ConfirmationCallback cc = (ConfirmationCallback) callbacks[i];

        confirmation.setCallback(cc);
        if (cc.getPrompt() != null) {
          messages.add(cc.getPrompt());
        }

      } else {
        throw new UnsupportedCallbackException(
            callbacks[i], "Unrecognized Callback");
      }
    }

        /* Display the dialog */
    int result = JOptionPane.showOptionDialog(
        parentComponent,
        messages.toArray(),
        "Confirmation",                     /* title */
        confirmation.optionType,
        confirmation.messageType,
        null,                               /* icon */
        confirmation.options,               /* options */
        confirmation.initialValue);         /* initialValue */

        /* Perform the OK actions */
    if (result == JOptionPane.OK_OPTION
        || result == JOptionPane.YES_OPTION) {
      Iterator<Action> iterator = okActions.iterator();
      while (iterator.hasNext()) {
        iterator.next().perform();
      }
    }
    confirmation.handleResult(result);
  }

  /*
   * Provides assistance with translating between JAAS and Swing
   * confirmation dialogs.
   */
  private static class ConfirmationInfo {

    private int[] translations;

    int optionType = JOptionPane.OK_CANCEL_OPTION;
    Object[] options = null;
    Object initialValue = null;

    int messageType = JOptionPane.QUESTION_MESSAGE;

    private ConfirmationCallback callback;

    /* Set the confirmation callback handler */
    void setCallback(ConfirmationCallback callback)
        throws UnsupportedCallbackException {
      this.callback = callback;

      int confirmationOptionType = callback.getOptionType();
      switch (confirmationOptionType) {
        case ConfirmationCallback.YES_NO_OPTION:
          optionType = JOptionPane.YES_NO_OPTION;
          translations = new int[]{
              JOptionPane.YES_OPTION, ConfirmationCallback.YES,
              JOptionPane.NO_OPTION, ConfirmationCallback.NO,
              JOptionPane.CLOSED_OPTION, ConfirmationCallback.NO
          };
          break;
        case ConfirmationCallback.YES_NO_CANCEL_OPTION:
          optionType = JOptionPane.YES_NO_CANCEL_OPTION;
          translations = new int[]{
              JOptionPane.YES_OPTION, ConfirmationCallback.YES,
              JOptionPane.NO_OPTION, ConfirmationCallback.NO,
              JOptionPane.CANCEL_OPTION, ConfirmationCallback.CANCEL,
              JOptionPane.CLOSED_OPTION, ConfirmationCallback.CANCEL
          };
          break;
        case ConfirmationCallback.OK_CANCEL_OPTION:
          optionType = JOptionPane.OK_CANCEL_OPTION;
          translations = new int[]{
              JOptionPane.OK_OPTION, ConfirmationCallback.OK,
              JOptionPane.CANCEL_OPTION, ConfirmationCallback.CANCEL,
              JOptionPane.CLOSED_OPTION, ConfirmationCallback.CANCEL
          };
          break;
        case ConfirmationCallback.UNSPECIFIED_OPTION:
          options = callback.getOptions();
                /*
                 * There's no way to know if the default option means
                 * to cancel the login, but there isn't a better way
                 * to guess this.
                 */
          translations = new int[]{
              JOptionPane.CLOSED_OPTION, callback.getDefaultOption()
          };
          break;
        default:
          throw new UnsupportedCallbackException(
              callback,
              "Unrecognized option type: " + confirmationOptionType);
      }

      int confirmationMessageType = callback.getMessageType();
      switch (confirmationMessageType) {
        case ConfirmationCallback.WARNING:
          messageType = JOptionPane.WARNING_MESSAGE;
          break;
        case ConfirmationCallback.ERROR:
          messageType = JOptionPane.ERROR_MESSAGE;
          break;
        case ConfirmationCallback.INFORMATION:
          messageType = JOptionPane.INFORMATION_MESSAGE;
          break;
        default:
          throw new UnsupportedCallbackException(
              callback,
              "Unrecognized message type: " + confirmationMessageType);
      }
    }


    /* Process the result returned by the Swing dialog */
    void handleResult(int result) {
      if (callback == null) {
        return;
      }

      for (int i = 0; i < translations.length; i += 2) {
        if (translations[i] == result) {
          result = translations[i + 1];
          break;
        }
      }
      callback.setSelectedIndex(result);
    }
  }
}
